#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
using std::ostream;

//核心思想：
//执行拷贝或者赋值的时候，默认执行的是浅拷贝操作
//只有当真正需要进行修改数据，也就是下标访问赋值的时候，那么才需要深拷贝
//引用计数和字符串是存储在一个堆空间的
//前4个字节用来去存储引用计数、后面的空间用来存储字符串
//拷贝：浅拷贝、引用计数 + 1
//赋值：浅拷贝，左操作数的引用计数 - 1(需要关注是否到0，到0就回收)、右操作数的引用计数 + 1
//析构：引用计数 - 1，到0则回收
class CowString{
public:
    CowString()
    :_pstr(new char[1 + 4]() + 4)
    {
        *(int *)(_pstr - 4) = 1;
    }
    CowString(const char * pstr)
    :_pstr(new char[strlen(pstr) + 1 + 4]() + 4)
    {
        strcpy(_pstr, pstr);
        *(int *)(_pstr - 4) = 1;

    }

    CowString(const CowString & rhs)
    :_pstr(rhs._pstr)
    {
        ++*(int *)(_pstr - 4);
    }

    CowString & operator=(const CowString & rhs){
        if(this != &rhs){
            if(--*(int *)(_pstr - 4) == 0){
                delete [] (_pstr - 4);
            }
            _pstr = rhs._pstr;
            ++*(int *)(_pstr - 4);
        }
        return *this;
    }

    int refcount() const {
        return *(int *)(_pstr - 4);
    }
    int size() const {
        return strlen(_pstr);
    }

    const char * c_str() const {
        return _pstr;
    }

private:
    //当前这个类的主要功能是为了给下标访问运算符使用的
    //外部不需要使用这个类，所以直接设置私有区域
    class CharProxy{
    public:
        CharProxy(CowString & str, size_t index)
        :_index(index)
        ,_str(str)
        {

        }
        friend ostream & operator<<(ostream & os, const CowString::CharProxy & rhs);
        //编写运算符重载
        //赋值运算符
        //思路：hello  str[0] = 'h'
        //1.还是需要进行自赋值的判断(指定下标位置的字符和给定的ch字符是否一致)
        //2.判断现有的引用计数是否大于1，true，左操作数引用计数 - 1，右操作数的引用计数 = 1
        //如果引用计数 = 1，直接修改即可
        char & operator=(const char & ch){
            //对于下标的合法性进行校验
            if(_index >= _str.size()){
                static char nullchar = '\0';
                return nullchar;
            }
            if(_str._pstr[_index] != ch){
                if(_str.refcount() > 1){
                    //大于1
                    char * temp = new char[strlen(_str._pstr) + 1 + 4]() + 4;
                    strcpy(temp, _str._pstr);
                    //原先的字符引用计数 - 1
                    --*(int *)(_str._pstr - 4);
                    _str._pstr = temp;
                    //现有的引用计数 = 1
                    *(int *)(_str._pstr - 4) = 1;
                }
                //等于1---直接修改即可
                _str._pstr[_index] = ch;
            }
            return _str._pstr[_index];
        }

    private:
        size_t _index;
        CowString & _str;
    };
    //设置为CowString的友元
    friend ostream & operator<<(ostream & os, const CowString::CharProxy & rhs);
public:
    //下标访问运算符返回的不再是一个char类型
    //而是一个类对象类型
    //参数需要改动：添加一个参数
    //将当前外部类对象传递给内部类
    CharProxy operator[](size_t index){
        return CharProxy(*this,index);
    }


private:
    char * _pstr;
};
//因为CharProxy位于CowString私有区域，所以首先需要设置为CowString的友元
//因为该函数需要访问rhs里面的私有数据成员_index，所以需要设置为CharProxy的友元
//应当输出指定下标位置的字符
//CharProxy持有_index下标
//CowString持有_pstr
//合在一起_pstr[_index]就是我们需要的数据
//需要在内部类中去持有外部类对象
ostream & operator<<(ostream & os, const CowString::CharProxy & rhs){
    if(rhs._index >= rhs._str.size()){
        static char nullchar = '\0';
        os << nullchar;
    }else {
        os << rhs._str._pstr[rhs._index];
    }
    return os;
}


void test(){
    CowString s1("hello");
    //CowString::CharProxy cp = s1[0];
    //cout << cp << endl;
    //cp = 'H';
    cout << s1[0] << endl;
    cout << s1[1] << endl;
    cout << s1[2] << endl;
    cout << s1[3] << endl;
    cout << s1[4] << endl;
    cout << s1[5] << endl;
    //s1[0] = 'H';
}

void test2(){
    CowString s1("hello");
    cout << s1[0] << endl;
    s1[0] = 'H';
    cout << s1[0] << endl;
    cout << "===============" << endl;
    CowString s2("helloworld");
    CowString s3 = s2;
    cout << s3.refcount() << endl;
    s3[0] = 'H';
    cout << s3.refcount() << endl;
    cout << s2.refcount() << endl;
    cout << s2.c_str() << endl;
    cout << s3.c_str() << endl;
}
void test3(){
    CowString s("hello");
    s[0];
}
int main()
{
    test2();
    return 0;
}

